#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

#include "sample_comm.h"

#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"

#include "HisiDecSendStream.h"


#define HISI_DEC_STACK_SIZE        (10 * 1024 * 1024)

typedef struct hiVDEC_USERPIC_S
{    
    HI_U32   u32PicWidth;
    HI_U32   u32PicHeigth;
    VB_POOL  u32PoolId;
    VB_BLK   u32BlkHandle;
    HI_U32   u32PhyAddr;
    HI_VOID  *pVirAddr;
}VDEC_USERPIC_S;


typedef struct
{
    OD_TASK_COMMON_MEMBERS

    uint32_t u32Chn;
    uint32_t u32PutFrameCnt;
    hbuf_handle hBufEs;
    uint32_t inEsSize;
    uint8_t *inEsBufPtr;
    bool picEna;
    uint32_t video_lost_cnt;
    VDEC_USERPIC_S stUserPic;

    /*tubes*/
    tube_handle ipcInEs;
}hisi_dec_obj;

/* dynamic_params */
params_def_t hisi_dec_dynamic_params_def[] = 
{
    {OFFSET(hisi_dec_dynamic_params, ctrl),          ODPT_VAR_INT32 | PT_REAL_TIME, sizeof(int), OD_OFF, OD_ON},
    {OFFSET(hisi_dec_dynamic_params, stream_width),  ODPT_VAR_INT32 | ODPT_READONLY, sizeof(int), 0, 10000},
    {OFFSET(hisi_dec_dynamic_params, stream_height), ODPT_VAR_INT32 | ODPT_READONLY, sizeof(int), 0, 10000},
    {0, 0, 0, 0, 0}
};

const hisi_dec_static_params glb_hisi_dec_static_params_default = 
{
    "hisi_decoder",  /*task_name*/
    0                /*u32Chn*/
};

const hisi_dec_dynamic_params glb_hisi_dec_dynamic_params_default = 
{
    OD_ON,        //ctrl
    1920,         //stream_width
    1080          //stream_height
};

static HI_VOID VDEC_PREPARE_USERPIC(VIDEO_FRAME_INFO_S *pstUsrPicInfo,  char *pFileName, VDEC_USERPIC_S* pstUserPic)
{
    HI_U32 u32PicSize;
    HI_S32 i, j; 
    HI_U32 u32YStride;
    HI_U8  *p, *puv, *pu, *pv;
    FILE* fpYUV;
    HI_U32 s32Ret = HI_SUCCESS;
    
    u32PicSize = ALIGN_UP(pstUserPic->u32PicWidth, 16)* ALIGN_UP(pstUserPic->u32PicHeigth, 16)* 3/2;  

    pstUserPic->u32BlkHandle = HI_MPI_VB_GetBlock(0, u32PicSize, "anonymous");
    if (VB_INVALID_HANDLE == pstUserPic->u32BlkHandle || HI_ERR_VB_ILLEGAL_PARAM == pstUserPic->u32BlkHandle)
    {
        SAMPLE_PRT("getBlock fail!\n");
        return;
    }	
    pstUserPic->u32PhyAddr = HI_MPI_VB_Handle2PhysAddr(pstUserPic->u32BlkHandle);
    pstUserPic->u32PoolId = HI_MPI_VB_Handle2PoolId(pstUserPic->u32BlkHandle);
    HI_MPI_VB_MmapPool(pstUserPic->u32PoolId);
    s32Ret = HI_MPI_VB_GetBlkVirAddr(pstUserPic->u32PoolId, pstUserPic->u32PhyAddr, &pstUserPic->pVirAddr);
    if(s32Ret != HI_SUCCESS)
    {	    
        HI_MPI_VB_ReleaseBlock(pstUserPic->u32BlkHandle);
        HI_MPI_VB_MunmapPool(pstUserPic->u32PoolId);
        SAMPLE_PRT("HI_MPI_VB_GetBlkVirAddr fail for %#x!\n", s32Ret);
        return;
    }

    puv = malloc(u32PicSize);
    HI_ASSERT(puv != HI_NULL);

    p = (HI_U8 *)pstUserPic->pVirAddr;
    u32YStride = (pstUserPic->u32PicWidth + 15) & ( ~(15) );
    fpYUV = fopen(pFileName,"rb");
    if (fpYUV == NULL)
    {
        printf("can't open file %s in VDEC_PREPARE_USERPIC.\n", pFileName);
    }    
    
    /* read the data of Y component*/
    fread(p, 1, pstUserPic->u32PicHeigth * u32YStride, fpYUV);

    /* read the data of UV component*/
    fread(puv, 1, (pstUserPic->u32PicHeigth * u32YStride) >> 1, fpYUV);
    pu = puv;
    pv = puv + ((pstUserPic->u32PicHeigth * u32YStride) >> 2);
    p  = pstUserPic->pVirAddr + (pstUserPic->u32PicHeigth * u32YStride);

    for (i = 0; i < (pstUserPic->u32PicHeigth >> 1); i++)
    {
        for (j=0; j<(pstUserPic->u32PicWidth >> 1); j++)
        {
            p[j*2+1] = pu[j];
            p[j*2+0] = pv[j];
        }

        pu += u32YStride >> 1;
        pv += u32YStride >> 1;
        p  += u32YStride;
    }

    free(puv);
    puv = HI_NULL;
    fclose(fpYUV);

    pstUsrPicInfo->u32PoolId = pstUserPic->u32PoolId;
    pstUsrPicInfo->stVFrame.enCompressMode = COMPRESS_MODE_NONE;
    pstUsrPicInfo->stVFrame.enVideoFormat = VIDEO_FORMAT_LINEAR;
    pstUsrPicInfo->stVFrame.enPixelFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    pstUsrPicInfo->stVFrame.u32Width = pstUserPic->u32PicWidth;
    pstUsrPicInfo->stVFrame.u32Height = pstUserPic->u32PicHeigth;
    pstUsrPicInfo->stVFrame.u32Field = VIDEO_FIELD_FRAME;
    pstUsrPicInfo->stVFrame.u32PhyAddr[0] = pstUserPic->u32PhyAddr;
    pstUsrPicInfo->stVFrame.u32PhyAddr[1] = pstUserPic->u32PhyAddr + (u32YStride * pstUserPic->u32PicHeigth);
    pstUsrPicInfo->stVFrame.u32Stride[0] = u32YStride;
    pstUsrPicInfo->stVFrame.u32Stride[1] = u32YStride;
    pstUsrPicInfo->stVFrame.u64pts = 0;    
}


static HI_VOID VDEC_RELEASE_USERPIC(VDEC_USERPIC_S* pstUserPic)
{
    HI_S32 s32Ret = HI_SUCCESS;

    s32Ret = HI_MPI_VB_ReleaseBlock(pstUserPic->u32BlkHandle);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VB_ReleaseBlock fail for %#x!\n", s32Ret);			
    }	 

    s32Ret = HI_MPI_VB_MunmapPool(pstUserPic->u32PoolId);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VB_MunmapPool fail for %#x!\n", s32Ret);			
    }		
}

static int hisi_dec_send_stream(hisi_dec_obj *obj)
{
    VDEC_STREAM_S stStream;
    HI_S32 retval = 0;

    stStream.u64PTS  = hbuf_get_ts(obj->hBufEs);
    stStream.pu8Addr = obj->inEsBufPtr;
    stStream.u32Len  = obj->inEsSize; 
    stStream.bEndOfFrame  = HI_FALSE;
    stStream.bEndOfStream = HI_FALSE;       

    retval = HI_MPI_VDEC_SendStream(obj->u32Chn, &stStream, -1);
    if (retval) {
        RPTERR("HI_MPI_VDEC_SendStream error, code:0x%x", retval);
        tube_put_buf(obj->ipcInEs, obj->hBufEs);
        return OD_ERROR;
    }

    obj->u32PutFrameCnt++;
    tube_put_buf(obj->ipcInEs, obj->hBufEs);

    RPTDBG("putFrameCnt :%u, len:%u, pts:%llu", 
        obj->u32PutFrameCnt, 
        obj->inEsSize, 
        stStream.u64PTS);
    return OD_OK;
}

static void hisi_dec_enable_pic(hisi_dec_obj *obj)
{
    VIDEO_FRAME_INFO_S stUsrPicInfo;
    HI_S32 s32Ret;

    if (obj->picEna == true)
        return;
    
    obj->stUserPic.u32PicWidth = 960;
    obj->stUserPic.u32PicHeigth = 576;
    VDEC_PREPARE_USERPIC(&stUsrPicInfo,  "logo.yuv", &obj->stUserPic);

    s32Ret = HI_MPI_VDEC_SetUserPic(obj->u32Chn, &stUsrPicInfo);
    if(s32Ret != HI_SUCCESS)
    {	
        RPTWRN("HI_MPI_VDEC_SetUserPic fail for %#x!\n", s32Ret);
        return;
    }	

    od_msleep(50);

    HI_MPI_VDEC_StopRecvStream(obj->u32Chn);		
    HI_MPI_VDEC_EnableUserPic(obj->u32Chn, HI_TRUE);

    obj->picEna = true;
}

static void hisi_dec_disable_pic(hisi_dec_obj *obj)
{
    if (obj->picEna == false)
        return;
    
    HI_MPI_VDEC_DisableUserPic(obj->u32Chn);		
    HI_MPI_VDEC_StartRecvStream(obj->u32Chn);

    VDEC_RELEASE_USERPIC(&obj->stUserPic);

    obj->picEna = false;
}

static int hisi_dec_start_stop(hisi_dec_obj *obj)
{
    obj->picEna = false;
    obj->video_lost_cnt = 0;

    return OD_OK;
}

static int hisi_dec_process(hisi_dec_obj *obj)
{
    hisi_dec_dynamic_params *params = (hisi_dec_dynamic_params *)obj->params;

    if (params->ctrl != OD_ON) {
        params->stream_width = 0;
        params->stream_height = 0;
        tube_flush(obj->ipcInEs);
        od_msleep(10);
        return OD_OK;
    }
    
    /*get es buf from network unpacket, check size and pointer */
    if(tube_get_buf(obj->ipcInEs, &obj->hBufEs)!= OD_OK) {
        if (obj->video_lost_cnt++ > 2) {
            hisi_dec_enable_pic(obj);
        }
        return OD_OK;
    }
    obj->video_lost_cnt = 0;
    hisi_dec_disable_pic(obj);
    
    obj->inEsSize = hbuf_get_usedsize(obj->hBufEs);
    obj->inEsBufPtr = hbuf_get_useptr(obj->hBufEs);
    if ( obj->inEsSize <= 0
         || obj->inEsBufPtr == NULL)
    {
        RPTERR("error inEsSize[%d]", obj->inEsSize);
        tube_put_buf(obj->ipcInEs, obj->hBufEs);
        return OD_ERROR;
    }

    return hisi_dec_send_stream(obj);
}

hisi_dec_handle hisi_dec_create(hisi_dec_static_params * static_params, 
                                hisi_dec_dynamic_params * dynamic_params)
{
    hisi_dec_obj *obj = NULL;
    hisi_dec_dynamic_params *params = NULL;
    linker_t *ins = NULL;
    tube_attr_t tube_attr = glb_tube_Attrs_default;

    assert(static_params);

    obj = (hisi_dec_obj *)od_task_create((char *)static_params->task_name, sizeof(hisi_dec_obj));
    if (NULL == obj)
    {
        RPTERR("Can't [%s] task! error!", static_params->task_name);
        return NULL;
    }

    obj->tab = hisi_dec_dynamic_params_def;
    obj->params_nums = sizeof(hisi_dec_dynamic_params_def) / sizeof(params_def_t) - 1;

    params = (hisi_dec_dynamic_params *)od_calloc(1, sizeof(hisi_dec_dynamic_params));
    if (NULL == params)
    {
        RPTERR("Can't create dynamic_params [%s] create error!", static_params->task_name);
        od_task_delete(obj);
        return NULL;
    }

    if(dynamic_params == NULL)
    {
        memcpy(params, &glb_hisi_dec_dynamic_params_default, sizeof(hisi_dec_dynamic_params)); 
    }
    else
    {
        memcpy(params, dynamic_params, sizeof(hisi_dec_dynamic_params)); 
    }

    obj->u32Chn = static_params->u32Chn;
    obj->params = params;
    obj->pf_rt_set = NULL;
    obj->pf_start_stop  = (void *)hisi_dec_start_stop;
    obj->pf_malloc_free = NULL;
    obj->pf_process     = (int (*)(od_task_handle))hisi_dec_process;
    obj->pf_start_stop_delete = NULL;
    obj->pf_malloc_free_delete = NULL;
    obj->stack_size = HISI_DEC_STACK_SIZE;

    /*Tube in create*/
    ins = &(obj->ins);
    ins->nums = 1;
    ins->tubes = (tube_handle *)od_calloc(ins->nums, sizeof(tube_handle));
    tube_attr.type = OD_TUBE_TYPE_CONSUMER;
    strcpy(tube_attr.name, DEC_SEND_STREAM_IN_ES_NAME);
    obj->ipcInEs = ins->tubes[0] = tube_create(&tube_attr);

    return (hisi_dec_handle)obj;
}


/*_*/

